#include "biplTypes.h"
#include <iostream>
#include <sstream>

/// implicitly convert to bool
boolTypes::operator bool() const
{
	return val;
}

/// implicitly convert to other types
boolTypes::operator intTypes() const
{
	return toInteger();
}

boolTypes::operator floatTypes() const
{
	return toFloat();
}

boolTypes::operator degreeTypes() const
{
	return toDegree();
}

boolTypes::operator pairTypes() const
{
	return toPair();
}

boolTypes::operator stringTypes() const
{
	return toString();
}

/// output stream operator
std::ostream&
	operator<<(std::ostream&out, const boolTypes& s)
{
	out << std::boolalpha << s.val;
	return out;
}

/// Negation Operator
intTypes operator- (const boolTypes &s)
{
	if (s.val)
		return intTypes(-1);
	else
		return intTypes(0);
}

/// Arithmetic Operators
const intTypes operator+(const boolTypes& lhs, const boolTypes& rhs)
{
	return intTypes(lhs.val + rhs.val);
}

const intTypes operator-(const boolTypes& lhs, const boolTypes& rhs)
{
	return intTypes(lhs.val - rhs.val);
}

const intTypes operator/(const boolTypes& lhs, const boolTypes& rhs)
{
	if (rhs.val == false) {
		std::cout << "Warning: Boolean type divided by false." << std::endl;
		return intTypes(1);
	}
	#pragma warning( suppress: 4804)
	return intTypes(lhs.val / rhs.val);
}

const intTypes operator*(const boolTypes& lhs, const boolTypes& rhs)
{
	return intTypes(lhs.val * rhs.val);
}

const intTypes operator%(const boolTypes& lhs, const boolTypes& rhs)
{
	if (rhs.val == false) {
		std::cout << "Warning: Boolean type modulus by false." << std::endl;
		return lhs.toInteger();
	}
	#pragma warning( suppress: 4804)
	return intTypes(lhs.val % rhs.val);
}

/// Arithmetic Assignment Operators
intTypes operator+=(boolTypes& lhs, const boolTypes& rhs)
{
	lhs.val = (lhs.val + rhs.val)? true : false;
	return lhs.toInteger();
}

intTypes operator-=(boolTypes& lhs, const boolTypes& rhs)
{
	if (lhs.val == rhs.val)
		lhs.val = false;
	else
		lhs.val = true;
	return lhs.toInteger();
}

intTypes operator*=(boolTypes& lhs, const boolTypes& rhs)
{
	if (rhs.val == false)
		lhs.val = false;
	return lhs.toInteger();
}

intTypes operator/=(boolTypes& lhs, const boolTypes& rhs)
{
	if (rhs.val == false) {
		std::cout << "Warning: Boolean type divided by false." << std::endl;
		lhs.val = true;
	}
	return lhs.toInteger();
}

intTypes operator%=(boolTypes& lhs, const boolTypes& rhs)
{
	if (rhs.val == true)
		lhs.val = false;
	else {
		std::cout << "Bipl Warning: Boolean type modulus by false." << std::endl;
	}
	return lhs.toInteger();
}

/// Equality Operators
boolTypes operator==(const boolTypes& left, const boolTypes& right)
{
	return boolTypes(left.val == right.val);
}

boolTypes operator!=(const boolTypes& left, const boolTypes& right)
{
	return boolTypes(left.val != right.val);
}

/// Relational Operators
boolTypes operator<(const boolTypes& left, const boolTypes& right)
{
	return boolTypes(left.val < right.val);
}

boolTypes operator>(const boolTypes& left, const boolTypes& right)
{
	return boolTypes(left.val > right.val);
}

boolTypes operator<=(const boolTypes& left, const boolTypes& right)
{
	return boolTypes(left.val <= right.val);
}

boolTypes operator>=(const boolTypes& left, const boolTypes& right)
{
	return boolTypes(left.val >= right.val);
}

boolTypes operator<(const boolTypes& left, const intTypes& right)
{
	std::cout << "Bipl Warning: '<' , unsafe data types 'bool'" << std::endl;
	return left.toInteger() < right;
}

boolTypes operator>(const boolTypes& left, const intTypes& right)
{
	std::cout << "Bipl Warning: '>' , unsafe data types 'bool'" << std::endl;
	return left.toInteger() > right;
}

boolTypes operator<=(const boolTypes& left, const intTypes& right)
{
	std::cout << "Bipl Warning: '<=' , unsafe data types 'bool'" << std::endl;
	return left.toInteger() <= right;
}

boolTypes operator>=(const boolTypes& left, const intTypes& right)
{
	std::cout << "Bipl Warning: '>=' , unsafe data types 'bool'" << std::endl;
	return left.toInteger()>= right;
}

boolTypes operator<(const intTypes& left, const boolTypes& right)
{
	std::cout << "Bipl Warning: '<' , unsafe data types 'bool'" << std::endl;
	return left < right.toInteger();
}

boolTypes operator>(const intTypes& left, const boolTypes& right)
{
	std::cout << "Bipl Warning: '>' , unsafe data types 'bool'" << std::endl;
	return left > right.toInteger();
}

boolTypes operator<=(const intTypes& left, const boolTypes& right)
{
	std::cout << "Bipl Warning: '<=' , unsafe data types 'bool'" << std::endl;
	return left > right.toInteger();
}

boolTypes operator>=(const intTypes& left, const boolTypes& right)
{
	std::cout << "Bipl Warning: '>=' , unsafe data types 'bool'" << std::endl;
	return left > right.toInteger();
}

boolTypes operator<(const boolTypes& left, const floatTypes& right)
{
	std::cout << "Bipl Warning: '<' , unsafe data types 'bool'" << std::endl;
	return left.toFloat() < right;
}

boolTypes operator>(const boolTypes& left, const floatTypes& right)
{
	std::cout << "Bipl Warning: '>' , unsafe data types 'bool'" << std::endl;
	return left.toFloat() > right;
}

boolTypes operator<=(const boolTypes& left, const floatTypes& right)
{
	std::cout << "Bipl Warning: '<=' , unsafe data types 'bool'" << std::endl;
	return left.toFloat() <= right;
}

boolTypes operator>=(const boolTypes& left, const floatTypes& right)
{
	std::cout << "Bipl Warning: '>=' , unsafe data types 'bool'" << std::endl;
	return left.toFloat()>= right;
}

boolTypes operator<(const floatTypes& left, const boolTypes& right)
{
	std::cout << "Bipl Warning: '<' , unsafe data types 'bool'" << std::endl;
	return left < right.toFloat();
}

boolTypes operator>(const floatTypes& left, const boolTypes& right)
{
	std::cout << "Bipl Warning: '>' , unsafe data types 'bool'" << std::endl;
	return left > right.toFloat();
}

boolTypes operator<=(const floatTypes& left, const boolTypes& right)
{
	std::cout << "Bipl Warning: '<=' , unsafe data types 'bool'" << std::endl;
	return left > right.toFloat();
}

boolTypes operator>=(const floatTypes& left, const boolTypes& right)
{
	std::cout << "Bipl Warning: '>=' , unsafe data types 'bool'" << std::endl;
	return left > right.toFloat();
}

/// Logical Operators
boolTypes operator!(const boolTypes& s)
{
	return boolTypes(!s.val);
}

boolTypes operator&&(const boolTypes& left, const boolTypes& right)
{
	return boolTypes(left.val && right.val);
}

boolTypes operator||(const boolTypes& left, const boolTypes& right)
{
	return boolTypes(left.val || right.val);
}

/// Type conversion functions
boolTypes boolTypes::toBoolean() const
{
	return boolTypes(val);
}

intTypes boolTypes::toInteger() const
{
	return intTypes(val);
}

floatTypes boolTypes::toFloat() const
{
	return floatTypes(float(val));
}

degreeTypes boolTypes::toDegree() const
{
	return degreeTypes(float(val));
}

pairTypes boolTypes::toPair() const
{
	return pairTypes(float(val), float(val));
}

stringTypes boolTypes::toString() const
{
	std::stringstream ss(std::stringstream::in | std::stringstream::out);
	ss << std::boolalpha << val;
	
	return stringTypes(ss.str());
}